/*落地模块的实现：
1.抽象出一个落地基类
2.派生出不同落地方向的子类
3.使用工厂模式进行创建和表示的分离*/
#ifndef _SINK_H_
#define _SINK_H_
#include "util.hpp"
#include<sstream>
#include<fstream>
namespace bitlog 
{
    class logsink
    {
        public:
        using ptr = std::shared_ptr<logsink>;
        virtual ~logsink(){};//因为用户可能扩展出不同的落地方向，申请的资源会有释放，所以将析构函数写为纯虚函数；
        virtual void logdirection(const char *data,size_t len)=0;
    };
    //标准输出方向
    class StdoutSink:public logsink
    {
        public:
        //将日志写入标准输出
        void logdirection(const char *data,size_t len)
        {
             std::cout.write(data,len);
        }
    };
    //指定文件落地方向
    class FileSink:public logsink
    {
        public:
        //构造时传入文件名，并打开文件，将操作句柄管理起来
        FileSink(const std::string &pathname)
        :_pathname(pathname)
        {
            //1.创建日志文件所在的目录
            util::File::createDirctory(util::File::filepath(pathname));
            //2.创建并打开日志文件
            _ofs.open(_pathname,std::ios::binary|std::ios::app);
            //若没有打开，直接报错推出
            assert(_ofs.is_open());

        }
        //将日志输出到指定文件中
        void logdirection(const char *data,size_t len)
        {
            _ofs.write(data,len);
            assert(_ofs.good());
        }
        private:
        std::string _pathname;
        std::ofstream _ofs;
    };
    //
    class RollBySizeSink :public logsink
    {
        public:
        //同样构造时传入文件名，并打开文件，并将操作句柄管理
        RollBySizeSink(const std::string &basename,size_t max_size)
        :_basename(basename),
        _max_size(max_size),
        _current_size(0),
        _namecount(0)
        {
            std::string pathname = createNewFile();
            //1.创建日志文件所在的目录
            util::File::createDirctory(util::File::filepath(pathname));
            //2.创建并打开日志文件
            _ofs.open(pathname,std::ios::binary|std::ios::app);
            //若没有打开，直接报错推出
            assert(_ofs.is_open());
        }
        //写入时还要判断一下文件的大小，超过最大大小时就要切换文件
        void logdirection(const char *data,size_t len)
        {
             if(_current_size>=_max_size)//若当前文件大小大于最大文件大小，创建新文件，关闭原来的文件
             {
                _ofs.close();
                std::string pathname= createNewFile();
                _ofs.open(pathname,std::ios::binary|std::ios::app);
                assert(_ofs.is_open());
                _current_size=0;
             }
             _ofs.write(data,len);
             assert(_ofs.good());
             _current_size+=len;
        }
        private: 
        //基础文件名加上扩展文件名组成实际文件名
        std::string createNewFile()
        {
            //得到系统时间后作为文件的拓展名称
            time_t qtime=util::Date::get_time();
            struct tm lt;
            localtime_r(&qtime,&lt);
            std::stringstream filename;
            filename<<_basename;
            filename<<lt.tm_year+1900;
            filename<<lt.tm_mon+1;
            filename<<lt.tm_mday;
            filename<<lt.tm_hour;
            filename<<lt.tm_min;
            filename<<lt.tm_sec;
            filename<<"-";
            filename<<_namecount++;
            filename<<".log";
            return filename.str();
        }
        std::string _basename;
        size_t _max_size; 
        size_t _current_size;
        std::ofstream _ofs;
        size_t _namecount;
    };
    
    class SinkFactory
    {
        public:
        template<typename SinkType,typename ...Args>//通过模板参数实现
        static logsink::ptr create(Args &&...args)
        {
            return std::make_shared<SinkType>(std::forward<Args>(args)...);
        }
    };
}

#endif